//+------------------------------------------------------------------+
//|																			OpenCL.h	|
//| Simple OpenCL wrapper.															|
//+------------------------------------------------------------------+
#pragma once

#include "Math.h"
#include <cl/cl.h>
#include <vector>

//+------------------------------------------------------------------+
class OpenCL
{
public:
//+------------------------------------------------------------------+
//	member data
	cl_int						error;
	cl_platform_id				platform[8];					// platform ids
	cl_device_id				device[8];						// device ids
	cl_uint						num_platforms;
	cl_uint						num_devices;
	cl_context					context;							// context handle
	cl_command_queue			queue;							// command queue handle
	std::vector<cl_program>	program;							// program handles
	std::vector<cl_kernel>	kernel;							// kernel handles
	std::vector<cl_mem>		mem;								// memory buffer handles
	size_t						totalMem;						// sum of all ever allocated memory buffers (cannot shrink atm...)

//+------------------------------------------------------------------+
	OpenCL(cl_device_type type = CL_DEVICE_TYPE_GPU, cl_command_queue_properties prop = CL_QUEUE_PROFILING_ENABLE);
	~OpenCL();

//+------------------------------------------------------------------+
//	Programs
	void programFromSource(const char *filename, int i = 0);
	void programBuild		 (int i = 0, const char *options = "-cl-denorms-are-zero -cl-mad-enable -cl-no-signed-zeros -cl-fast-relaxed-math");
	void programSaveBinary(const char *filename, int i = 0);
	bool programFromBinary(const char *filename, int i = 0);

//+------------------------------------------------------------------+
//	Kernel
	bool kernelCreate		(int index = 0, const char *name = NULL, int program = 0);
	bool kernelSetArg		(int index, cl_uint argIndex, size_t argSize, const void *arg_value);
	bool kernelSetArgMem	(int index, cl_uint argIndex, int memIndex);
	bool kernelEnqueue1D	(int index, size_t global_work_size, size_t local_work_size = 0, cl_event *e = NULL);
	int  kernelWorkGroupSize(int i);

//+------------------------------------------------------------------+
//	Memory buffer: mem_flags are from the kernels perspective!
	bool		bufferAllocate	(int i, size_t size, cl_mem_flags = CL_MEM_WRITE_ONLY);
	bool		bufferAllocate	(int i, void *src, size_t, cl_mem_flags = CL_MEM_READ_ONLY | CL_MEM_COPY_HOST_PTR);
	void		bufferRead		(int i, void *dst, size_t, cl_bool block = CL_TRUE, cl_event *e = NULL);
	void		bufferWrite		(int i, void *src, size_t, cl_bool block = CL_TRUE, cl_event *e = NULL);
	void*		bufferMap		(int i, cl_map_flags flags = CL_MAP_READ, cl_bool block = CL_TRUE, cl_event *e = NULL);
	void		bufferUnmap		(int i, void *ptr, cl_event *e = NULL);
	void		bufferRelease	(int i);
	size_t	bufferInfo		(int i, cl_mem_info param_name = CL_MEM_SIZE);

//+------------------------------------------------------------------+
//	misc.
	bool flush();
	bool finish();

//+------------------------------------------------------------------+
//	Static utilities
	static void			PrintDeviceName(cl_device_id);
	static void			PrintDeviceInfo(cl_device_id);
	static cl_int		EventWait		(cl_event);
	static cl_int		EventInfo		(cl_event, cl_event_info = CL_EVENT_COMMAND_EXECUTION_STATUS);
	static cl_ulong	EventStartTime	(cl_event);
	static cl_ulong	EventEndTime	(cl_event);

};//class OpenCL

